#ifndef TREE_NODE_H
#define TREE_NODE_H

#include <cstdint>
#include <stdexcept>

namespace ColorTree
{
    class TreeNode
    {
    /**
     * color bit rgb
     * 0 for    r 0 g 0 b 0
     * 1 for    r 0 g 0 b 1
     * 2 for    r 0 g 1 b 0
     * 3 for    r 0 g 1 b 1
     * 4 for    r 1 g 0 b 0
     * 5 for    r 1 g 0 b 1
     * 6 for    r 1 g 1 b 0
     * 7 for    r 1 g 1 b 1
     */
    private:
        TreeNode* subNode[8] = {
                nullptr,
                nullptr,
                nullptr,
                nullptr,
                nullptr,
                nullptr,
                nullptr,
                nullptr
        };
        TreeNode* parentNode = nullptr;
        int depth = 0;
    public:
        explicit TreeNode(TreeNode* parent = nullptr, int depth = 0) : parentNode(parent), depth(depth) {};
        ~TreeNode()
        {
            for (auto& i : subNode)
                delete i;
        }
        inline TreeNode& GetOrCreatSubNode(int index)
        {
            if (index > 7)
                throw std::out_of_range("index must in range 0~7");
            if (subNode[index] == nullptr)
                subNode[index] = new TreeNode(this, depth + 1);
            return *subNode[index];
        }
        inline TreeNode* GetSubNode(int index)
        {
            if (index > 7)
                throw std::out_of_range("index must in range 0~7");
            return subNode[index];
        }
        inline int SubNodeCount()
        {
            int count = 0;
            for (auto& i : subNode)
                if (i != nullptr)
                    count++;
            return count;
        }

    // for distance estimate
    private:
        uint8_t bitColor{};

        static uint8_t DistanceTable[8][8];

    public:
        [[nodiscard]] inline uint8_t GetDistanceWith(const TreeNode& other) const
        {
            return DistanceTable[bitColor][other.bitColor];
        }
        inline void SetBitColor(uint8_t bc) { bitColor = bc; }

    // for area size estimate
    private:
        uint64_t counter = 0;
    public:
        inline void AddCounter()
        {
            counter++;
            if (parentNode != nullptr)
                parentNode->AddCounter();
        }
        [[nodiscard]] inline uint64_t GetCounter() const { return counter; }
    };
}
#endif //TREE_NODE_H
